/**
 * Copyright (C) 2008 haiko <hvdschaaf@gmail.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 *
 */
package nl.cybersnippet.easycomparator;

/**
 * A substitute for the method {@link Comparable#compareTo(T)} with basically
 * the same functionality, but giving readable methods instead of
 * operators. Underneath it uses the functionality of the compareTo method and
 * therefore the same restrictions must be adhered to use an implementation of
 * this interface. An operand is an input argument for an operator.
 * 
 * @author haiko
 * 
 * @param <T>
 *            the type of object that the {@link Operand} may be compared to.
 */
public final class Operand<T extends Comparable<? super T>>
		 {

	/**
	 * Comparable for comparison.
	 */
	private final Comparable<T> comparable;

	/**
	 * Comparable compared against.
	 */
	private T comparedAgainst;

	/**
	 * Private constructor so only the class itself can create a
	 * {@link Operand}.
	 * 
	 * @param comparableArg
	 *            An Object of type T and {@link Comparable} which is compared
	 *            against another entity of the same type.
	 */
	private Operand(final Comparable<T> comparableArg) {
		this.comparable = comparableArg;
	}

	/**
	 * Create new {@link Operand}. An operand is an input argument for an
	 * operator.
	 * 
	 * @param <T>
	 *            type of Object.
	 * @param comparable
	 *            Object which class implements {@link Comparable}.
	 * @return {@link Operand} for comparison against another Object of the same
	 *         type.
	 */
	public static <T extends Comparable<? super T>> Operand<T> operand(
			final Comparable<T> comparable) {
		return new Operand<T>(comparable);
	}

	/**
	 * Sets the other {@link Comparable} for comparison. The comparison is
	 * relating the objects to each other defined by the implementation of the
	 * {@link Comparable#compareTo(Object)}.
	 * 
	 * @param comparedAgainst
	 *            other {@link Comparable}.
	 * @return The same {@link Operand} for further comparison.
	 */
	public Operand<T> inRelationTo(final T comparedAgainstArg) {
		this.comparedAgainst = comparedAgainstArg;
		return this;
	}

	/**
	 * Checks if {@link Operand} is less in relation to argument comparedAgainst
	 * in the order defined by the implementation of the {@link Comparable}
	 * interface.
	 * 
	 * @return <code>true</code> if {@link Comparable} is less in the imposed
	 *         ordening by the {@link Comparable#compareTo(T)}. Otherwise,
	 *         <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isLesser() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(this.comparedAgainst) < 0;
	}

	/**
	 * Checks if {@link Operand} is equal in relation to argument
	 * comparedAgainst in the order defined by the implementation of the
	 * {@link Comparable} interface.
	 * 
	 * @return<code>true</code> if {@link Comparable} is equal in the imposed
	 *                          ordening by the {@link Comparable#compareTo(T)}.
	 *                          Otherwise, <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isEqual() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(comparedAgainst) == 0;
	}

	/**
	 * Checks if {@link Operand} is not equal in relation to argument
	 * comparedAgainst in the order defined by the implementation of the
	 * {@link Comparable} interface.
	 * 
	 * @return<code>true</code> if {@link Comparable} is not equal in the
	 *                          imposed ordening by the
	 *                          {@link Comparable#compareTo(T)}. Otherwise,
	 *                          <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isNotEqual() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(comparedAgainst) != 0;
	}

	/**
	 * Checks if {@link Operand} is greater in relation to argument
	 * comparedAgainst in the order defined by the implementation of the
	 * {@link Comparable} interface.
	 * 
	 * @return <code>true</code> if {@link Comparable} is greater in the imposed
	 *         ordening by the {@link Comparable#compareTo(T)}. Otherwise,
	 *         <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isGreater() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(comparedAgainst) > 0;
	}

	/**
	 * Checks if {@link Operand} is less or equal in relation to argument
	 * comparedAgainst in the order defined by the implementation of the
	 * {@link Comparable} interface.
	 * 
	 * @return <code>true</code> if {@link Comparable} is lesser or equal in the
	 *         imposed ordening by the {@link Comparable#compareTo(T)}.
	 *         Otherwise, <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isLesserOrEqual() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(comparedAgainst) <= 0;
	}

	/**
	 * Checks if {@link Operand} is greater or equal in relation to argument
	 * comparedAgainst in the order defined by the implementation of the
	 * {@link Comparable} interface.
	 * 
	 * @return <code>true</code> if {@link Comparable} is lesser or equal in the
	 *         imposed ordening by the {@link Comparable#compareTo(T)}.
	 *         Otherwise, <code>false</code> is returned.
	 * 
	 * @throws IllegalStateException
	 *             if comparedAgainst is not set.
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Boolean isGreaterOrEqual() {
		nullCheck(comparedAgainst);
		return this.comparable.compareTo(comparedAgainst) >= 0;
	}

	/**
	 * Compare the {@link Operand} to given object. This method returns a more
	 * convenient {@link Enum} instead of an int as compareTo in {@link Comparable} does. 
	 * 
	 * @param comparedAgainst
	 *            the object to be compared against.
	 * @return The result of the comparison as an {@link Order}.
	 * 
	 * @throws ClassCastException
	 *             if objects are not comparable.
	 */
	public Order compareTo(final T comparedAgainstArg) {
		nullCheck(comparedAgainstArg);

		int result = this.comparable.compareTo(comparedAgainstArg);

		if (result < 0) {
			return Order.LESSER;
		} else if (result == 0) {
			return Order.EQUAL;
		} else if (result > 0) {
			return Order.GREATER;
		}

		throw new IllegalStateException(
				"this exception can never be throwed, logically");
	}

	/**
	 * Checks if argument is not null;
	 * 
	 * @param comparedAgainstArg
	 *            Object to compare {@link Operand} against.
	 */
	private void nullCheck(final T comparedAgainstArg) {
		if (comparedAgainstArg == null) {
			throw new IllegalStateException(
					"No other operand to compare against");
		}
	}
}
